package com.vtuzx.kingdee.xqc_api.service.impl;

import com.mongodb.client.result.DeleteResult;
import com.vtuzx.core.VtuzxException;
import com.vtuzx.core.bean.dto.VtuzxMap;
import com.vtuzx.core.bean.dto.VtuzxPage;
import com.vtuzx.core.bean.dto.VtuzxToken;
import com.vtuzx.core.constant.IVtuzxConst;
import com.vtuzx.core.file.excel.VtuzxExcel;
import com.vtuzx.core.file.excel.VtuzxExcelCell;
import com.vtuzx.core.file.excel.VtuzxExcelRow;
import com.vtuzx.core.file.excel.VtuzxExcelSheet;
import com.vtuzx.core.utils.VtuzxUtil;
import com.vtuzx.fast.utils.FastUtil;
import com.vtuzx.kingdee.xqc_api.service.IXqcPurContractService;
import com.vtuzx.kingdee.xqc_core.bean.VtuzxArrayList;
import com.vtuzx.kingdee.xqc_core.constant.BillStatus;
import com.vtuzx.kingdee.xqc_core.constant.BillType;
import com.vtuzx.kingdee.xqc_core.constant.IXqcConst;
import com.vtuzx.kingdee.xqc_core.dao.*;
import com.vtuzx.web.bean.VtuzxDownloadBean;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 采购合同
 */
@Service
public class XqcPurContractServiceImpl implements IXqcPurContractService {

    /**
     * Logger
     */
    private static final Logger _logger = LoggerFactory.getLogger(XqcPurContractServiceImpl.class);

    @Autowired
    private PurContractDao purContractDao;

    @Autowired
    private PurContractDetailDao purContractDetailDao;

    @Autowired
    private SupplierDao supplierDao;

    @Autowired
    private PurOrderDao purOrderDao;

    @Autowired
    private PurOrderDetailDao purOrderDetailDao;

    @Autowired
    private MaterialDao materialDao;

    @Override
    public VtuzxMap savePurCon(ObjectId _userId, String name, Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");

        Document headerDoc = new Document();
        headerDoc.append("billDate", VtuzxUtil.getString(param, "billDate"));
        headerDoc.append("startDate", VtuzxUtil.getString(param, "startDate"));
        headerDoc.append("endDate", VtuzxUtil.getString(param, "endDate"));

        headerDoc.append("purchaseDept", VtuzxUtil.getString(param, "purchaseDept"));
        headerDoc.append("purchaseDeptName", VtuzxUtil.getString(param, "purchaseDeptName"));
        headerDoc.append("purchaser", VtuzxUtil.getString(param, "purchaser"));
        headerDoc.append("purchaserName", VtuzxUtil.getString(param, "purchaserName"));
        headerDoc.append("supplierNo", VtuzxUtil.getString(param, "supplierNo"));
        headerDoc.append("supplierName", VtuzxUtil.getString(param, "supplierName"));

        headerDoc.append("charge", VtuzxUtil.getString(param, "charge"));
        headerDoc.append("settle", VtuzxUtil.getString(param, "settle"));
        headerDoc.append("remark", VtuzxUtil.getString(param, "remark"));

        List<Map<String, Object>> dataList = VtuzxUtil.getObject(param, "dataList");

        if (VtuzxUtil.isEmpty(billNo)) {
            billNo = IXqcConst.createBillNo(BillType.CGHT);
            headerDoc.append("billNo", billNo);
            headerDoc.append("status", BillStatus.Save.toString());

            purContractDao.insert(_userId, name, headerDoc);
            // 更新分录
            updatePurContractDetail(_userId, name, billNo, dataList);

            return new VtuzxMap("billNo", billNo);
        } else {
            purContractDao.update(_userId, name, billNo, headerDoc);
            // 更新分录
            updatePurContractDetail(_userId, name, billNo, dataList);
            return new VtuzxMap("billNo", billNo);
        }
    }

    @Override
    public void deletePurCon(String billNo) throws VtuzxException {
        DeleteResult res = purContractDao.deleteOne(Criteria.where("billNo").is(billNo).and("status").is(BillStatus.Save.toString()));
        if (res.getDeletedCount() == 0) {
            throw new VtuzxException(IVtuzxConst.Msg_E_target_not_exist);
        }
        purContractDetailDao.deleteMany(Criteria.where("billNo").is(billNo));
    }

    @Override
    public void auditPurCon(ObjectId _userId, String name, String billNo) {
        purContractDao.audit(_userId, name, billNo);
    }

    @Override
    public void unAuditPurCon(String billNo) throws VtuzxException {
        long orderCount = purOrderDao.count(Criteria.where("billNo").is(billNo));
        if (orderCount > 0) {
            throw new VtuzxException("存在下游单据, 不能反审!");
        }
        purContractDao.unAudit(billNo);
    }

    @Override
    public VtuzxMap getPurConList(Map<String, Object> param, int pageIndex, int pageSize) {
        Criteria cond = getPurConListCond(param);
        VtuzxPage page = purContractDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        List<VtuzxMap> dataList = page.iterator(itr -> {
            VtuzxMap data = new VtuzxMap(itr);
            data.append(IVtuzxConst.Key_createTime, VtuzxUtil.formatYmdHmF(VtuzxUtil.removeObject(data, IVtuzxConst.Key_createTime)));
            data.remove(IVtuzxConst.Key_updateTime);
            data.append("auditTime", VtuzxUtil.formatYmdHmF(VtuzxUtil.removeObject(data, "auditTime")));
            data.remove("_createUserId");
            data.remove("_updateUserId");
            data.remove("_auditUserId");
            return data;
        });
        return new VtuzxMap(IVtuzxConst.Key_total, page.total()).append(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public VtuzxMap getPurCon(String billNo) throws VtuzxException {
        Document doc = purContractDao.findOne(Criteria.where("billNo").is(billNo));
        if (doc == null) {
            throw new VtuzxException(IVtuzxConst.Msg_E_target_not_exist);
        }
        VtuzxMap header = new VtuzxMap(doc);
        Iterator<Document> boxItr = purContractDetailDao.findList(Criteria.where("billNo").is(billNo), new Document(IVtuzxConst.Key__id, 1), 0, 0);
        List<VtuzxMap> dataList = new ArrayList<>();
        while (boxItr.hasNext()) {
            Document boxDoc = boxItr.next();
            VtuzxMap data = new VtuzxMap(boxDoc);
            data.append("id", VtuzxUtil.removeString(data, IVtuzxConst.Key__id));
            data.remove(IVtuzxConst.Key_createTime);
            data.remove(IVtuzxConst.Key_updateTime);
            data.remove("_createUserId");
            data.remove("createUserName");
            data.remove("_updateUserId");
            data.remove("updateUserName");
            dataList.add(data);
        }
        return new VtuzxMap("header", header).append(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public VtuzxDownloadBean downloadPurCon(Map<String, Object> param) throws Exception {
        Criteria cond = getPurConListCond(param);
        Iterator<Document> purConItr = purContractDao.findPurContractList(cond);
        List<VtuzxMap> dataList = new ArrayList<>();
        while (purConItr.hasNext()) {
            Document doc = purConItr.next();
            VtuzxMap data = new VtuzxMap(doc);
            Document detail = VtuzxUtil.getObject(doc, "detail");
            data.putAll(detail);
            dataList.add(data);
        }

        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());

        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "采购合同");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++, "单据编码"),
                new VtuzxExcelCell(col++, "合同日期"),
                new VtuzxExcelCell(col++, "起始日期"),
                new VtuzxExcelCell(col++, "截止日期"),
                new VtuzxExcelCell(col++, "采购部门"),
                new VtuzxExcelCell(col++, "采购员"),
                new VtuzxExcelCell(col++, "供应商"),
                new VtuzxExcelCell(col++, "收款方"),
                new VtuzxExcelCell(col++, "结算方"),
                new VtuzxExcelCell(col++, "物料编码"),
                new VtuzxExcelCell(col++, "物料名称"),
                new VtuzxExcelCell(col++, "规格型号"),
                new VtuzxExcelCell(col++, "采购单位"),
                new VtuzxExcelCell(col++, "计价单位"),
                new VtuzxExcelCell(col++, "合同数量"),
                new VtuzxExcelCell(col++, "计价数量"),
                new VtuzxExcelCell(col++, "交货日期"),
                new VtuzxExcelCell(col++, "单价"),
                new VtuzxExcelCell(col++, "含税单价"),
                new VtuzxExcelCell(col++, "折扣率"),
                new VtuzxExcelCell(col++, "金额"),
                new VtuzxExcelCell(col++, "税率"),
                new VtuzxExcelCell(col++, "税额"),
                new VtuzxExcelCell(col++, "价税合计"),
                new VtuzxExcelCell(col++, "结算组织"),
                new VtuzxExcelCell(col++, "到货数量"),
                new VtuzxExcelCell(col, "结算金额")
        ));
        sheet0.addRow(header0);

        for (VtuzxMap detail : dataList) {
            col = 0;
            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "billNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "billDate")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "startDate")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "endDate")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "purchaseDeptName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "purchaserName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "supplierName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "charge")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "settle")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialSpec")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "purchaseUnitName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "calcUnitName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "qty")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "calcQty")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "deliveryDate")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "price")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "taxPrice")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "discountRate")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "amount")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "taxRateStr")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "tax")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "taxAmount")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "settleOrgName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "settlementCount")),
                    new VtuzxExcelCell(col, VtuzxUtil.getString(detail, "settlementAmount"))
            ));
            sheet0.addRow(dataRow);
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[]{0, 200},
                new int[]{1, 200},
                new int[]{2, 200},
                new int[]{3, 300},
                new int[]{4, 200},
                new int[]{5, 200},
                new int[]{6, 150},
                new int[]{7, 150},
                new int[]{8, 150},
                new int[]{9, 150},
                new int[]{10, 150},
                new int[]{11, 150},
                new int[]{12, 150},
                new int[]{13, 150},
                new int[]{14, 150},
                new int[]{15, 150},
                new int[]{16, 150},
                new int[]{17, 150},
                new int[]{18, 150},
                new int[]{19, 150},
                new int[]{20, 150},
                new int[]{21, 150},
                new int[]{22, 150},
                new int[]{23, 150},
                new int[]{24, 150},
                new int[]{25, 150},
                new int[]{26, 150},
                new int[]{27, 80}
        ));
        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean("采购合同" + timestamp + ".xlsx", stream);
    }

    @Override
    public void pushPurConOrder(ObjectId _userId, String name, Map<String, Object> param) throws VtuzxException {
        String billNo = VtuzxUtil.getString(param, "billNo");
        String orderNo = IXqcConst.createBillNo(BillType.CGDD);
        String remark = VtuzxUtil.getString(param, "remark");
        String orderType = VtuzxUtil.getString(param, "orderType");
        Document doc = purContractDao.findOne(Criteria.where("billNo").is(billNo));
        if (doc == null) {
            throw new VtuzxException(IVtuzxConst.Msg_E_target_not_exist);
        }
        purOrderDao.insertOne(new Document("orderNo", orderNo)
                .append("billNo", billNo)
                .append("orderType", orderType)
                .append("remark", remark)
                .append("status", BillStatus.Save.toString())
                .append("billDate", VtuzxUtil.getString(doc, "billDate"))
                .append("startDate", VtuzxUtil.getString(doc, "startDate"))
                .append("endDate", VtuzxUtil.getString(doc, "endDate"))
                .append("purchaseDept", VtuzxUtil.getString(doc, "purchaseDept"))
                .append("purchaseDeptName", VtuzxUtil.getString(doc, "purchaseDeptName"))
                .append("purchaser", VtuzxUtil.getString(doc, "purchaser"))
                .append("purchaserName", VtuzxUtil.getString(doc, "purchaserName"))
                .append("supplierNo", VtuzxUtil.getString(doc, "supplierNo"))
                .append("supplierName", VtuzxUtil.getString(doc, "supplierName"))
                .append("charge", VtuzxUtil.getString(doc, "charge"))
                .append("settle", VtuzxUtil.getString(doc, "settle")));
        Iterator<Document> boxItr = purContractDetailDao.findList(Criteria.where("billNo").is(billNo), null, 0, 0);
        List<Document> dataList = new ArrayList<>();
        while (boxItr.hasNext()) {
            Document detailDoc = boxItr.next();
            Document orderDetailDoc = new Document();
            orderDetailDoc.append("_billDetailId", VtuzxUtil.getObject(detailDoc, IVtuzxConst.Key__id));
            orderDetailDoc.append("billNo", billNo);
            orderDetailDoc.append("orderNo", orderNo);
            orderDetailDoc.append("materialNo", VtuzxUtil.getObject(detailDoc, "materialNo"));
            orderDetailDoc.append("materialName", VtuzxUtil.getObject(detailDoc, "materialName"));
            orderDetailDoc.append("materialSpec", VtuzxUtil.getObject(detailDoc, "materialSpec"));
            orderDetailDoc.append("batchNo", "");
            orderDetailDoc.append("purchaseUnitName", VtuzxUtil.getObject(detailDoc, "purchaseUnitName"));
            orderDetailDoc.append("calcUnitName", VtuzxUtil.getObject(detailDoc, "calcUnitName"));
            orderDetailDoc.append("deliveryDate", VtuzxUtil.parseDate(VtuzxUtil.getObject(detailDoc, "deliveryDate")));
            orderDetailDoc.append("taxRateStr", VtuzxUtil.getObject(detailDoc, "taxRateStr"));
            orderDetailDoc.append("taxRate", VtuzxUtil.getDouble(detailDoc, "taxRate"));
            orderDetailDoc.append("qty", VtuzxUtil.getInt(detailDoc, "qty"));
            orderDetailDoc.append("unSettlementQty", VtuzxUtil.getInt(detailDoc, "qty"));
            orderDetailDoc.append("price", VtuzxUtil.getDouble(detailDoc, "price"));
            orderDetailDoc.append("amount", VtuzxUtil.getDouble(detailDoc, "amount"));
            orderDetailDoc.append("discountRate", VtuzxUtil.getDouble(detailDoc, "discountRate"));
            orderDetailDoc.append("taxPrice", VtuzxUtil.getDouble(detailDoc, "taxPrice"));
            orderDetailDoc.append("taxAmount", VtuzxUtil.getDouble(detailDoc, "taxAmount"));
            orderDetailDoc.append("tax", VtuzxUtil.getDouble(detailDoc, "tax"));
            orderDetailDoc.append("isGift", "0");
            dataList.add(orderDetailDoc);
        }
        purOrderDetailDao.insertMany(dataList);
    }

    @Override
    public VtuzxMap getMaterial(VtuzxToken token, String key, int pageSize) {
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(key)) {
            if (key.startsWith("06")) {
                cond.and("fNumber").regex("^" + key + ".*$", "i");
            } else if ("06".startsWith(key)) {
                cond.and("fNumber").regex("^06.*$", "i");
            } else if (key.startsWith("05")) {
                cond.and("fNumber").regex("^" + key + ".*$", "i");
            } else if ("05".startsWith(key)) {
                cond.and("fNumber").regex("^05.*$", "i");
            } else {
//                cond.and("fNumber").regex("^06.*$");
//                cond.and("fNumber").regex("^05.*$");
                cond.orOperator(Criteria.where("fNumber").regex("^05.*$", "i"), Criteria.where("fNumber").regex("^06.*$", "i"));
                cond.and("fName").regex("^.*" + key + ".*$", "i");
            }
        } else {
            cond.orOperator(Criteria.where("fNumber").regex("^05.*$", "i"), Criteria.where("fNumber").regex("^06.*$", "i"));
//            cond.and("fNumber").regex("^06.*$");
//            cond.and("fNumber").regex("^05.*$");
            cond.and("fName").regex("^.*" + key + ".*$", "i");
        }
        VtuzxPage page = materialDao.page(cond, new Document("fNumber", 1), 0, 0);
        List<VtuzxMap> dataList = page.iterator(doc ->
                new VtuzxMap("id", VtuzxUtil.getString(doc, IVtuzxConst.Key__id))
                        .append("fName", VtuzxUtil.getString(doc, "fName"))
                        .append("fNumber", VtuzxUtil.getString(doc, "fNumber"))
                        .append("fSpecification", VtuzxUtil.getString(doc, "fSpecification"))
                        .append("fBaseUnitIdFName", VtuzxUtil.getString(doc, "fBaseUnitIdFName"))
                        .append("FEntryTaxRate",VtuzxUtil.getString(doc,"fTaxRateName")));

        return new VtuzxMap(IVtuzxConst.Key_data, dataList);
    }

    /**
     * 更新采购合同
     */
    private void updatePurContractDetail(ObjectId _userId, String name, String billNo, List<Map<String, Object>> dataList) {
        List<ObjectId> _idList = new ArrayList<>();
        for (Map<String, Object> data : dataList) {
            Document detailDoc = new Document(data);
            detailDoc.append("billNo", billNo);
            String id = VtuzxUtil.removeString(detailDoc, "id");
            if (VtuzxUtil.isEmpty(id)) {
                ObjectId _id = purContractDetailDao.insert(_userId, name, detailDoc);
                _idList.add(_id);
            } else {
                ObjectId _id = FastUtil.convertId(id);
                purContractDetailDao.update(_userId, name, _id, detailDoc);
                _idList.add(_id);
            }
        }
        // 多余行删除
        Criteria cond = Criteria.where("billNo").is(billNo);
        cond.and(IVtuzxConst.Key__id).nin(_idList);
        purContractDetailDao.deleteMany(cond);
    }

    /**
     * 采购合同列表检索条件
     *
     * @param param 输入条件
     * @return 检索条件
     */
    private Criteria getPurConListCond(Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");
        List<String> billDateList = VtuzxUtil.getObject(param, "billDate");
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(billNo)) {
            cond.and("billNo").regex(billNo);
        }
        if (!VtuzxUtil.isEmpty(billDateList)) {
            cond.and("billDate").gte(billDateList.get(0)).lte(billDateList.get(1));
        }
        return cond;
    }
}
